#include <Ice/Application.h>
#include <FileTransfer.h>
#include <time.h>

using namespace std;
using namespace Ice;
using namespace FileTransfer;

class FileStore_readI
  : public AMI_FileStore_read,
    public IceUtil::Monitor<IceUtil::Mutex>
{
public:
  FileStore_readI() : _done(false)
  {
  }
  virtual void ice_response(const ::std::pair<const ::Ice::Byte*, const ::Ice::Byte*>&);
  virtual void ice_exception(const ::Ice::Exception&);
  void getData(::Ice::ByteSeq&);
private:
  bool _done;
  auto_ptr<Ice::Exception> _exception;
  Ice::ByteSeq _bytes;
};

typedef IceUtil::Handle<FileStore_readI> FileStore_readIPtr;

void
FileStore_readI :: ice_response(const ::std::pair<const ::Ice::Byte*, const ::Ice::Byte*>& bytes)
{
  Lock sync(*this);
  Ice::ByteSeq(bytes.first, bytes.second).swap(_bytes);
  _done = true;
  notify();
}

void
FileStore_readI :: ice_exception(const ::Ice::Exception& ex)
{
  Lock sync(*this);
  _exception.reset(ex.ice_clone());
  _done = true;
  notify();
}

void
FileStore_readI :: getData(::Ice::ByteSeq& bytes)
{
  Lock sync(*this);
  while (!_done) {
    wait();
  }
  _done = false;
  if (_exception.get()) {
    auto_ptr<Ice::Exception> ex = _exception;
    _bytes.clear();
    ex->ice_throw();
  }
  bytes.swap(_bytes);
}

class Client
  : virtual public Ice::Application
{
public:
  virtual int run (int argc,
		   char *argv[])
  {
    Ice::ObjectPrx base = communicator()->
      stringToProxy("FileStore:tcp -h 161.67.38.113 -p 10000");
    FileStorePrx fileStorePrx = FileStorePrx::checkedCast(base);

    if (argc < 2) {
      cout << "Synopsis: ./Client <file_to_transfer>" << endl;
      return EXIT_FAILURE;
    }

    string name = argv[1];
    FILE* fp = fopen(name.c_str(), "wb");
    Ice::ByteSeq bytes;
    int offset = 0;
    int len = 1000 * 1024;
    int rc = EXIT_SUCCESS;
    time_t begin = 0, end = 0;
    FileStore_readIPtr curr, next;

    begin = time(NULL);

    for (;;) {
      if (!curr) {
	curr = new FileStore_readI;
	next = new FileStore_readI;
	fileStorePrx->read_async(curr, name, offset, len);
      }
      else {
	swap(curr, next);
      }
      fileStorePrx->read_async(next, name, offset + len, len);
      curr->getData(bytes);
      if (bytes.empty()) {
	break;
      }
      if (fwrite(&bytes[0], 1, bytes.size(), fp)
	  != bytes.size()) {
	cerr << "error writing: " << strerror(errno) << endl;
	rc = EXIT_FAILURE;
      }
      offset += bytes.size(); 
    }

    fclose(fp);
    end = time(NULL);

    cout << "Seconds employed: " << end - begin << endl;

    communicator()->waitForShutdown();
    return rc;
  }
};

int main(int argc,
	 char* argv[])
{
  Client client;
  return client.main(argc, argv);
}
